#include "scene_parser.h"
#include "image.h"
#include "camera.h"
#include "group.h"
#include "light.h"
#include "material.h"
#include "raytracer.h"
#include "sampleDebugger.h"
#include <time.h>
#include <pthread.h>
#include <cstring>
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
//#include <iostream>

#define N 1000
#define MEGEXTRA 1000000

using namespace std;
using namespace d_Math;


char *input_file = NULL;
int width = 100;
int height = 100;
char *output_file = NULL;
float depth_min = 0;
float depth_max = 1;
char *depth_file = NULL;
char *normals_file = NULL;
char *samples_file = NULL;
//RayTracer stuff
bool shadows = false;
int bounces = 0;
float weight = 1;
//Path-tracing variables
int ptsamples = 1;
bool ispathtracing = false;
//Anti-Aliasing
int numofsamples = 1;
int zoomfactor = 1;
bool isuniform = false;
bool isjittering = false;
float boxradius = 0;
//OpenGL
bool isopengl = false;

Camera *camera;
RayTracer *tracer;
SampleDebugger *sampledebugger;

Image *image;
Image *dimage;
Image *nimage;

d_Vec3f *pixels;

//void *trace_thread(void *ptr);
//pthread_attr_t attr;

void setOrtho(int w, int h) 
{
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0, w, 0, h);
	//glScalef(1, -1, 1);
	//glTranslatef(0, -h, 0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

int main(int argc, char **argv)
{
	////////////Command line parsing///////////////
	for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i],"-input")) {
	    i++; assert (i < argc); 
	    input_file = argv[i];
	  } else if (!strcmp(argv[i],"-size")) {
	    i++; assert (i < argc); 
	    width = atoi(argv[i]);
	    i++; assert (i < argc); 
	    height = atoi(argv[i]);
	  } else if (!strcmp(argv[i],"-output")) {
	    i++; assert (i < argc); 
	    output_file = argv[i];
	  } else if (!strcmp(argv[i],"-depth")) {
	    i++; assert (i < argc); 
	    depth_min = atof(argv[i]);
	    i++; assert (i < argc); 
	    depth_max = atof(argv[i]);
	    i++; assert (i < argc); 
	    depth_file = argv[i];
	  } else if (!strcmp(argv[i], "-normals")) {
		i++; assert (i < argc);
		normals_file = argv[i];
	  } else if (!strcmp(argv[i], "-shadows")) {	
		shadows = true;
	  } else if (!strcmp(argv[i], "-bounces")) {
		i++; assert (i < argc);
		bounces = atoi(argv[i]);
	  } else if (!strcmp(argv[i], "-weight")) {
		i++; assert (i < argc);
		weight = atof(argv[i]);
	  } else if (!strcmp(argv[i], "-uniform_samples")) {
		isuniform = true;
		i++; assert (i < argc);
		numofsamples = atoi(argv[i]);
	  } else if (!strcmp(argv[i], "-jittered_samples")) {
		isjittering = true;
		i++; assert (i < argc);
		numofsamples = atoi(argv[i]);
	  } else if (!strcmp(argv[i], "-render_samples")) {
		i++; assert (i < argc);
		samples_file = argv[i];
		i++; assert (i < argc);
		zoomfactor = atoi(argv[i]);
	  } else if (!strcmp(argv[i], "-box_filter")) {
		i++; assert (i < argc);
		boxradius = atoi(argv[i]);
	  } else if (!strcmp(argv[i], "-opengl")) {
		isopengl = true;
	  }else if (!strcmp(argv[i], "-path_tracing")){
	  	i++; assert (i < argc);
	  	ispathtracing = true;
	  	ptsamples = atoi(argv[i]);
	  } else {
	    printf ("whoops error with command line argument %d: '%s'\n",i,argv[i]);
	    assert(0);
	  }
	}
	////////////////////////////////////////////

	if(output_file!=NULL)
		image = new Image(width, height);
	if(normals_file!=NULL)
		nimage = new Image(width, height);
	if(depth_file!=NULL)
		dimage = new Image(width, height);

	//pixels = new Vec3f[width*height];

	SceneParser *scene = new SceneParser(input_file);
	tracer = new RayTracer(scene, bounces, weight, shadows, ispathtracing);
	camera = scene->getCamera();

	sampledebugger = new SampleDebugger(width, height, numofsamples);
	
	SDL_Surface *screen;
	if(isopengl)
	{
		SDL_Init(SDL_INIT_VIDEO);
		screen = SDL_SetVideoMode(width,height,24,SDL_OPENGL|SDL_GL_DOUBLEBUFFER);
		setOrtho(width,height);
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	}
	
	srand ( time(NULL) );

	
	for (int j = 0; j< height; j++) 
	{
		for (int i=0; i< width; i++) 
		{
			
			float x = (float)i/(float)width;
			float y = (float)j/(float)height;
			Hit hit(10000, NULL, d_Vec3f(0,0,0));
			Ray cast = camera->generateRay(d_Vec2f(x, y));
			

			//cout << i << " , " << j << " , " << endl;

			d_Vec3f color = d_Vec3f(0,0,0);
						
			if(ispathtracing)
			{
				for(int i=0; i<ptsamples; i++)
				{
					Hit pt_hit(10000, NULL, d_Vec3f(0,0,0));
					Ray pt_cast = camera->generateRay(d_Vec2f(x, y));
					color = color + tracer->traceRay(pt_cast, camera->getTMin(), bounces, weight, pt_hit);
				}
				color = color/ptsamples;
			}
			else
				color = tracer->traceRay(cast, camera->getTMin(), bounces, weight, hit);

			if(isopengl)
			{
				glBegin(GL_POINTS);
				glColor3f(color[0],color[1],color[2]);
				glVertex3f(i,j,0);
				glEnd();
				//SDL_GL_SwapBuffers();
				
			}
			
			if(normals_file != NULL)
			{
				d_Vec3f normal = hit.getNormal();
				if(normal[0]<0)
					normal[0] = -normal[0];
				if(normal[1]<0)
					normal[1] = -normal[1];
				if(normal[2]<0)
					normal[2] = -normal[2];
				normal = d_Vec3f(normal[0] , normal[1], normal[2]);
				nimage->SetPixel(i, j, normal);
			}
			if(depth_file != NULL)
			{
				float depth;
				if(hit.getT()>depth_max)
					depth=0;
				else if(hit.getT()<depth_min)
					depth=1;
				else
					depth = (hit.getT()-depth_max)/(depth_min-depth_max);
				dimage->SetPixel(i, j, d_Vec3f(depth,depth,depth));
			}
			if(numofsamples>1)
			{
				Sample samplespp[numofsamples];
				ctime(NULL);
				int ind = 0;
				float wnum = 1/sqrt(numofsamples);
				for(float l=0; l<1; l+=wnum)
					for(float k=0; k<1 ; k+=wnum)
					{
						Hit tmphit(10000, NULL, d_Vec3f(0,0,0));
						Ray sampleray=cast;
						if(isuniform)
						{
							sampleray = camera->generateRay(d_Vec2f(x+(wnum/2+k)/(float)width, y+(wnum/2+l)/(float)height));
							d_Vec3f samplecolor = tracer->traceRay(sampleray,camera->getTMin(),bounces,weight,tmphit);
							samplespp[ind].set(d_Vec2f(wnum/2+k, wnum/2+l),samplecolor);
							sampledebugger->setSample(i,j,ind, d_Vec2f(wnum/2+k, wnum/2+l), samplecolor);
						}
						if(isjittering)
						{
							float randomx = (float)random()/RAND_MAX*3/(2*numofsamples);//-1/(2*numofsamples);
							float randomy = (float)random()/RAND_MAX*3/(2*numofsamples);//-1/(2*numofsamples);
							sampleray = camera->generateRay(d_Vec2f(x+(wnum/2+k+randomx)/(float)width, y+(wnum/2+l+randomy)/(float)height));
							d_Vec3f samplecolor = tracer->traceRay(sampleray,camera->getTMin(),bounces,weight,tmphit);
							d_Vec2f p(wnum/2+k+randomx, wnum/2+l+randomy);
							if(p[0]<0)
								p[0]=0;
							if(p[0]>1)
								p[0]=1;
							if(p[1]<0)
								p[1]=0;
							if(p[1]>1)
								p[1]=1;
							samplespp[ind].set(p, samplecolor);
							sampledebugger->setSample(i,j,ind, p, samplecolor);

						}
						ind++;
					}
				d_Vec3f promedio = d_Vec3f(0,0,0);
				for(int k=0; k<numofsamples; k++)
				{
					promedio = promedio + samplespp[k].getColor();
				}
				promedio = promedio/numofsamples;
				if(output_file!=NULL)
					image->SetPixel(i,j,promedio);
			}
			else
			{
				if(output_file!=NULL)
					image->SetPixel(i,j,color);
			}
			if(isopengl)
				if(j%(int)((float)height/10.0)==0 && i==0)
					SDL_GL_SwapBuffers();
		}
	}
	
	
	cout << "Saving to: " << output_file << endl;

	if(output_file!=NULL)
		image->SaveTGA(output_file);
	if(normals_file!=NULL)
		nimage->SaveTGA(normals_file);
	if(depth_file!=NULL)
		dimage->SaveTGA(depth_file);
	if(samples_file!=NULL)
		sampledebugger->renderSamples(samples_file,zoomfactor);

	return 0;	
}

